package node.exp;

import grammar.Lexer;
import lexical.Key;
import lexical.Word;
import midcode.MidCode;
import midcode.MidcodeGenerator;
import midcode.OPType;
import node.Node;
import symbol.SymbolTable;

import java.util.ArrayList;

public class AddExp implements Node {
    private final Lexer lexer;
    private final SymbolTable table;
    private final Node father;
    private final ArrayList<MulExp> mulExps = new ArrayList<>();
    private final ArrayList<Word> calTypes = new ArrayList<>();

    public AddExp(Lexer lexer, SymbolTable table, Node father) {
        this.lexer = lexer;
        this.table = table;
        this.father = father;
        parseAddExp();
    }

    private void parseAddExp() {
        calTypes.add(new Word("first", Key.PLUS, -1));
        mulExps.add(new MulExp(lexer, table, this));
        while (lexer.isWordType(0, Key.PLUS) || lexer.isWordType(0, Key.MINU)) {
            calTypes.add(lexer.getCurWord()); // + | -
            mulExps.add(new MulExp(lexer, table, this));
        }
    }

    public int getValue() {
        int value = 0;
        for (int i = 0; i < mulExps.size(); i++) {
            if (calTypes.get(i).key == Key.PLUS) {
                value += mulExps.get(i).getValue();
            } else {
                value -= mulExps.get(i).getValue();
            }
        }
        return value;
    }

    @Override
    public Node getFather() {
        return father;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(mulExps.get(0).toString());
        for (int i = 1; i < mulExps.size(); i++) {
            sb.append(calTypes.get(i).toString());
            sb.append(mulExps.get(i).toString()).append("<AddExp>\n");
        }
        return sb.toString();
    }

    @Override
    public String genMidCode() {
        MidcodeGenerator mg = MidcodeGenerator.getInstance();
        ArrayList<String> mulTmp = new ArrayList<>();
        ArrayList<Word> calTmp = new ArrayList<>();
        int value = 0;
        for (int i = 0; i < mulExps.size(); i++) {
            String s = mulExps.get(i).genMidCode();
            if (mg.isNumber(s)) {
                value = (calTypes.get(i).key == Key.PLUS) ? value + Integer.parseInt(s) : value - Integer.parseInt(s);
            } else {
                mulTmp.add(s);
                calTmp.add(calTypes.get(i));
            }
        }
        String tmp1 = Integer.toString(value);
        for (int i = 0; i < mulTmp.size(); i++) {
            OPType opType = (calTmp.get(i).key == Key.PLUS) ? OPType.ADD : OPType.SUB;
            String tmp2 = mulTmp.get(i);
            String tmp3;
            if (mg.isNumber(tmp1) && Integer.parseInt(tmp1) == 0) {
                tmp3 = tmp2;
            } else {
                tmp3 = mg.genTmp();
                mg.addMidcode(new MidCode(opType, tmp1, tmp2, tmp3));
            }
            tmp1 = tmp3;
        }
        return tmp1;
    }

    private String genMidCode2() {
        MidcodeGenerator mg = MidcodeGenerator.getInstance();
        String tmp1 = mulExps.get(0).genMidCode();
        for (int i = 1; i < mulExps.size(); i++) {
            String tmp2 = mulExps.get(i).genMidCode();
            OPType opType = (calTypes.get(i).key == Key.PLUS) ? OPType.ADD : OPType.SUB;
            String tmp3;
            if (mg.isNumber(tmp1) && mg.isNumber(tmp2)) {
                int val1 = Integer.parseInt(tmp1);
                int val2 = Integer.parseInt(tmp2);
                if (opType == OPType.ADD) {
                    tmp3 = Integer.toString(val1 + val2);
                } else {
                    tmp3 = Integer.toString(val1 - val2);
                }
            } else if (mg.isNumber(tmp1) && Integer.parseInt(tmp1) == 0) {
                tmp3 = tmp2;
            } else if  (mg.isNumber(tmp2) && Integer.parseInt(tmp2) == 0) {
                tmp3 = tmp1;
            } else {
                tmp3 = mg.genTmp();
                mg.addMidcode(new MidCode(opType, tmp1, tmp2, tmp3));
            }
            tmp1 = tmp3;
        }
        return tmp1;
    }
}

